/**
 *    @file       cmysql.cpp
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       12/20/2012 09:15:50 AM
 *
 *    @author     Theophilus (), wangtf418@gmail.com
 */
#ifndef __WITHOUT_MYSQL_DB__
#include "cmysql.h"

threadmutex cmysql::m_initlock;

cmysql::cmysql()
	: m_ifconnected(0), m_result(NULL), m_row(NULL), m_row_len(NULL), m_field_num(0), m_row_num(0), m_ifthrow(1)
{

}

cmysql::cmysql(const mysql_conf &conf)
	: m_conf(conf), m_ifconnected(0), m_result(NULL), m_row(NULL), m_row_len(NULL),  m_field_num(0), m_row_num(0), m_ifthrow(1)
{

}

cmysql::~cmysql()
{
	this->close();
}

bf_int_t cmysql::init()
{
	bf_int_t ret = BF_OK;
	this->close();

	m_initlock.lock();
	if (mysql_init(&m_connection) == NULL)
	{
		ret = BF_ERROR;
	}
	m_initlock.unlock();

	if (ret != BF_OK)
	{
		set_error("init mysql fail", "", 0);
		throw_exception();
	}

	return ret;
}

bf_int_t cmysql::connect(bf_uint_t connect_timeout /* = BF_UINT_MAX */)
{
	bf_int_t ret = BF_OK;
	if (m_ifconnected)
	{
		this->close();
		ret = this->init();
	}

	if (ret != BF_OK)
	{
		return BF_ERROR;
	}

	if (connect_timeout != BF_UINT_MAX)
	{
		ret = set_option_connect_timeout(connect_timeout);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
	}

	MYSQL *conn_ret = mysql_real_connect(&m_connection, m_conf.m_host.c_str(), m_conf.m_user.c_str(), m_conf.m_passwd.c_str(), NULL, 0, NULL, CLIENT_MULTI_STATEMENTS);
	if (!conn_ret)
	{
		set_error(mysql_error(&m_connection), "", mysql_errno(&m_connection));
		throw_exception();
		ret = BF_ERROR;
	}
	m_ifconnected = 1;
	return ret;
}

bf_int_t cmysql::connect(const mysql_conf &conf, bf_uint_t connect_timeout /*  = BF_UINT_MAX */)
{
	m_conf = conf;
	return this->connect(connect_timeout);
}

void cmysql::close()
{
	if (m_ifconnected)
	{
		free_result();
		mysql_close(&m_connection);
		m_ifconnected = 0;
	}
}

bf_int_t cmysql::query(const std::string &sql)
{
	bf_int_t ret = BF_OK;
	if (m_result)
	{
		free_result();
	}
	ret = mysql_real_query(&m_connection, sql.c_str(), sql.length());
	if (ret != 0)
	{
		ret = BF_ERROR;
		set_error(mysql_error(&m_connection), sql.c_str(), mysql_errno(&m_connection));
		throw_exception();
	}
	return ret;
}

bf_int_t cmysql::fetch_row()
{
	bf_int_t ret = BF_OK;
	if (!m_result)
	{
		ret = store_result();
	}

	if (ret != BF_OK)
	{
		return BF_ERROR;
	}

	m_row = mysql_fetch_row(m_result);
	if (m_row)
	{
		m_row_len = mysql_fetch_lengths(m_result);
	}
	else
	{
		ret = BF_ERROR;
		if (mysql_errno(&m_connection) != 0)
		{
			set_error(mysql_error(&m_connection), "", mysql_errno(&m_connection));
			throw_exception();
		}
	}
	return ret;
}

std::string cmysql::get_fieldname(bf_uint_t field_index)
{
	if (m_result==NULL)
	{
		return std::string("");
	}
	MYSQL_FIELD *fields = mysql_fetch_fields(m_result);
	bf_uint_t field_num = mysql_num_fields(m_result);
	if (field_index >= field_num)
	{
		return std::string("");
	}
	return std::string(fields[field_index].name, fields[field_index].name_length);
}

bf_int_t cmysql::get_field(const std::string &fieldname, std::string &filecontent_out)
{
	std::map<std::string, bf_uint_t>::iterator itr = m_fieldnames.find(fieldname);
	if (itr == m_fieldnames.end())
	{
		return BF_ERROR;
	}
	if (itr->second >= m_field_num)
	{
		return BF_ERROR;
	}
	filecontent_out.assign(m_row[itr->second], m_row_len[itr->second]);
	return BF_OK;
}

bf_char_t* cmysql::get_field(const std::string &fieldname)
{
	std::map<std::string, bf_uint_t>::iterator itr = m_fieldnames.find(fieldname);
	if (itr == m_fieldnames.end())
	{
		return NULL;
	}
	if (itr->second >= m_field_num)
	{
		return NULL;
	}
	return m_row[itr->second];
}

bf_ullong_t cmysql::get_lastinsertid()
{
	return mysql_insert_id(&m_connection);	
}

void cmysql::escape_string(std::string &str)
{
	if (str.empty())
	{
		return;
	}
	bf_char_t *buf = new bf_char_t[str.size()*2+1];
	bf_ulong_t len = mysql_real_escape_string (&m_connection, buf, str.c_str(), str.size());
	str = std::string(buf, len);
	delete [] buf;
}

bf_int_t cmysql::set_option(enum mysql_option option, const bf_char_t *arg)
{
	bf_int_t ret = mysql_options(&m_connection, option, arg);
	if (ret != 0)
	{
		set_error("set_option", "", 0);
		throw_exception();
		return BF_ERROR;
	}
	return BF_OK;
}

bf_int_t cmysql::set_option_connect_timeout(bf_uint_t connect_timeout)
{
	return set_option(MYSQL_OPT_CONNECT_TIMEOUT, (bf_char_t *)&connect_timeout);
}

bf_int_t cmysql::set_option_read_timeout(bf_uint_t read_timeout)
{
	return set_option(MYSQL_OPT_READ_TIMEOUT, (bf_char_t *)&read_timeout);
}

bf_int_t cmysql::set_option_write_timeout(bf_uint_t write_timeout)
{

	return set_option(MYSQL_OPT_WRITE_TIMEOUT, (bf_char_t *)&write_timeout);
}

bf_int_t cmysql::set_option_auto_reconnect(my_bool auto_connect_flag)
{
	return set_option(MYSQL_OPT_RECONNECT, (bf_char_t*)&auto_connect_flag);
}

bf_int_t cmysql::store_result()
{
	bf_int_t ret = BF_OK;
	free_result();
	m_result = mysql_store_result(&m_connection);
	if (m_result == NULL)
	{
		ret = BF_ERROR;
		set_error(mysql_error(&m_connection), "", mysql_errno(&m_connection));
		throw_exception();
	}
	m_field_num = mysql_num_fields(m_result);
	m_row_num = mysql_num_rows(m_result);
	init_fieldname();
	return ret;
}

void cmysql::free_result()
{
	if (m_result)
	{
		mysql_free_result(m_result);
		m_result = NULL;
		m_row = NULL;
		m_row_len = 0;
		m_field_num = 0;
		m_row_num = 0;
		m_fieldnames.clear();
	}
}

bf_int_t cmysql::init_fieldname()
{
	if (!m_result)
	{
		return BF_ERROR;
	}
	MYSQL_FIELD *fields = mysql_fetch_fields(m_result);
	for (bf_uint_t i = 0; i < m_field_num; i ++)
	{
		m_fieldnames[std::string(fields[i].name, fields[i].name_length)] = i;
	}
	return BF_OK;
}

void cmysql::set_error(const std::string &errmsg, const std::string &sql, bf_int_t error_no)
{
	m_error = mysql_exception(sql, errmsg, error_no);
}

void cmysql::throw_exception()
{
	if (m_ifthrow)
	{
		throw m_error;
	}
}

#endif //end ifndef __WITHOUT_MYSQL_DB__

